package lolbatfsm.implementation.gui;

import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JOptionPane;
import lolbatfsm.system.AsynchronousTransition;
import lolbatfsm.system.EquivalenceClass;
import lolbatfsm.system.FSM;
import lolbatfsm.system.State;
import lolbatfsm.system.SynchronousTransition;
import lolbatfsm.system.Transition;

/**
 *  ItemPanel contains all the objects like comboboxes, checkboxes, buttons
 *  and textField used to show and edit one FSM's transition.
 * @author Fabio Lola <fadan87@gmail.com>
 */
public class ItemPanel extends javax.swing.JPanel implements ActionListener, MouseListener, ItemListener {

    private javax.swing.JCheckBox syncCheck;
    private javax.swing.JButton removeButton;
    private javax.swing.JComboBox sourceStateCombo;
    private javax.swing.JComboBox destinationStateCombo;
    private javax.swing.JTextField durationField;
    private javax.swing.JComboBox eqClassCombo;
    private Transition transition;
    private String[] states;
    private String[] equivalenceClasses;
    private boolean first;
    Set<EquivalenceClass> equivalenceClassesSet;
    HashSet<State> statesSet;
    MainForm mainForm;
    FSM ownerFSM;
    Integer index = 0;

    /**
     * 
     * @param _transition the itempanel's Transition
     * @param _states all the FSM's states
     * @param _equivalenceClass all the Equivalence classes
     * @param _mainForm the mainForm which contains ItemPanel
     * @param _first is the first transition
     */
    public ItemPanel(Transition _transition,
            HashSet<State> _states,
            Set<EquivalenceClass> _equivalenceClass,
            MainForm _mainForm,
            boolean _first) {
        transition = _transition;
        mainForm = _mainForm;
        statesSet = _states;
        states = new String[_states.size()];
        equivalenceClassesSet = _equivalenceClass;
        equivalenceClasses = new String[equivalenceClassesSet.size()];
        int i = 0;
        for (State state : _states) {
            states[i] = state.getName();
            i++;
        }
        i = 0;
        for (EquivalenceClass equivaleClass : _equivalenceClass) {
            equivalenceClasses[i] = equivaleClass.getName();
            i++;
        }
        first = _first;
        ownerFSM = _transition.getOwnerFSM();
        index = transition.getIndex();
        createGrid();
    }
    /**
     * This method creates all itemPanel's objects like comboboxes, checkboxes
     * and textfields
     */
    private void createGrid() {
        sourceStateCombo = new javax.swing.JComboBox();
        destinationStateCombo = new javax.swing.JComboBox();
        syncCheck = new javax.swing.JCheckBox();
        durationField = new javax.swing.JTextField();
        eqClassCombo = new javax.swing.JComboBox();
        removeButton = new javax.swing.JButton();
        java.awt.GridBagConstraints gridBagConstraints;
        setLayout(new java.awt.GridBagLayout());
        setMinimumSize(new java.awt.Dimension(520, 40));
        setPreferredSize(new java.awt.Dimension(690, 40));

        sourceStateCombo.setModel(new javax.swing.DefaultComboBoxModel(states));
        sourceStateCombo.setSelectedItem(transition.getSourceState().getName());
        sourceStateCombo.setMinimumSize(new java.awt.Dimension(120, 20));
        sourceStateCombo.setPreferredSize(new java.awt.Dimension(120, 20));
        sourceStateCombo.setEditable(first);
        sourceStateCombo.addActionListener(this);
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
        gridBagConstraints.weightx = 0.5;
        gridBagConstraints.weighty = 0.5;
        gridBagConstraints.insets = new java.awt.Insets(5, 10, 5, 10);
        add(sourceStateCombo, gridBagConstraints);

        destinationStateCombo.setModel(new javax.swing.DefaultComboBoxModel(states));
        destinationStateCombo.setSelectedItem(transition.getDestinationState().getName());
        destinationStateCombo.setMinimumSize(new java.awt.Dimension(120, 20));
        destinationStateCombo.setPreferredSize(new java.awt.Dimension(120, 20));
        destinationStateCombo.setEditable(true);
        destinationStateCombo.addActionListener(this);
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
        gridBagConstraints.weightx = 0.5;
        gridBagConstraints.weighty = 0.5;
        gridBagConstraints.insets = new java.awt.Insets(5, 10, 5, 10);
        add(destinationStateCombo, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
        gridBagConstraints.weightx = 0.5;
        gridBagConstraints.weighty = 0.5;
        gridBagConstraints.insets = new java.awt.Insets(5, 10, 5, 10);
        syncCheck.setSelected(transition instanceof SynchronousTransition);
        syncCheck.addItemListener(this);
        add(syncCheck, gridBagConstraints);

        durationField.setText(Integer.toString(transition.getResidualDuration()));
        durationField.setMinimumSize(new java.awt.Dimension(40, 20));
        durationField.setPreferredSize(new java.awt.Dimension(40, 20));
        durationField.addActionListener(this);
        if (transition instanceof SynchronousTransition) {
            durationField.setEditable(false);
        } else {
            durationField.setEditable(true);
        }
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
        gridBagConstraints.weightx = 0.5;
        gridBagConstraints.weighty = 0.5;
        gridBagConstraints.insets = new java.awt.Insets(5, 10, 5, 10);
        add(durationField, gridBagConstraints);

        if (transition instanceof SynchronousTransition) {
            eqClassCombo.setEnabled(true);
            eqClassCombo.setModel(new javax.swing.DefaultComboBoxModel(equivalenceClasses));
            eqClassCombo.addItem("Add new");
            eqClassCombo.setSelectedItem(((SynchronousTransition) transition).getEquivalenceClass().getName());
        } else {
            eqClassCombo.setEnabled(false);
        }
        eqClassCombo.addActionListener(this);
        eqClassCombo.setMinimumSize(new java.awt.Dimension(120, 20));
        eqClassCombo.setPreferredSize(new java.awt.Dimension(120, 20));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 4;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
        gridBagConstraints.weightx = 0.5;
        gridBagConstraints.weighty = 0.5;
        gridBagConstraints.insets = new java.awt.Insets(5, 10, 5, 10);
        add(eqClassCombo, gridBagConstraints);

        removeButton.setText("-");
        removeButton.setFont(new Font("Arial", Font.BOLD, 24));
        removeButton.setMaximumSize(new java.awt.Dimension(23, 20));
        removeButton.setMinimumSize(new java.awt.Dimension(41, 23));
        removeButton.setPreferredSize(new java.awt.Dimension(41, 23));
        removeButton.addMouseListener(this);
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 5;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
        gridBagConstraints.weightx = 0.5;
        gridBagConstraints.weighty = 0.5;
        gridBagConstraints.insets = new java.awt.Insets(3, 10, 3, 10);
        add(removeButton, gridBagConstraints);
    }
    /**
     * Returns a set of Equivalence Classes
     */
    public Set<EquivalenceClass> getEquivalenceClasses() {
        return equivalenceClassesSet;
    }

    /**
     * This method return all the FSM's states updated with the itemPanel's
     * changes
     */
    public Collection<State> getStates() {
        boolean modifiedSource = true;
        boolean modifiedDestination = true;
        if (sourceStateCombo.getSelectedItem().toString().equals("")
                || sourceStateCombo.getSelectedItem().toString().matches(" *")
                || destinationStateCombo.getSelectedItem().toString().equals("")
                || destinationStateCombo.getSelectedItem().toString().matches(" *")) {
            return null;
        } else {
            for (State state : statesSet) {
                if (state.getName().equals(sourceStateCombo.getSelectedItem())) {
                    modifiedSource = false;
                }
                if (state.getName().equals(destinationStateCombo.getSelectedItem())) {
                    modifiedDestination = false;
                }
            }
            if (destinationStateCombo.getSelectedItem().equals(sourceStateCombo.getSelectedItem())) {
                modifiedDestination = false;
            }
            if (modifiedSource) {
                statesSet.add(new State(sourceStateCombo.getSelectedItem().toString()));
            }
            if (modifiedDestination) {
                statesSet.add(new State(destinationStateCombo.getSelectedItem().toString()));
            }
            return statesSet;
        }
    }

    /**
     * Returns the selected Destination State
     */
    public String getDestinationStateString() {
        try {
            if (Integer.parseInt(durationField.getText()) <= 0) {
                return null;
            }
        } catch (NumberFormatException exception) {
            return null;
        }
        return (String) destinationStateCombo.getSelectedItem();
    }

    /**
     * This method return a transition updated with the itemPanel's changes
     */
    public Transition getTransition() {
        State sourceState = new State(sourceStateCombo.getSelectedItem().toString());
        State destinationState = new State(destinationStateCombo.getSelectedItem().toString());
        for (State state : statesSet) {
            if (state.getName().equals(sourceStateCombo.getSelectedItem().toString())) {
                sourceState = state;
            }
            if (state.getName().equals(destinationStateCombo.getSelectedItem().toString())) {
                destinationState = state;
            }
        }
        try {
            if (Integer.parseInt(durationField.getText()) <= 0) {
                return null;
            }
        } catch (NumberFormatException exception) {
            return null;
        }
        if (!syncCheck.isSelected()) {
            transition = new AsynchronousTransition(sourceState,
                    destinationState,
                    Integer.parseInt(durationField.getText()));
        } else {
            EquivalenceClass equivalenceClass = new EquivalenceClass((String) eqClassCombo.getSelectedItem(), Integer.parseInt(durationField.getText()));
            for (EquivalenceClass _equivalenceClass : equivalenceClassesSet) {
                if (eqClassCombo.getSelectedItem().equals(_equivalenceClass.getName())) {
                    equivalenceClass = _equivalenceClass;
                }
            }
            if(equivalenceClass.getName().equals("Add new")){
                JOptionPane.showMessageDialog(null, "Void equivalence class, please select one", "ERROR", JOptionPane.ERROR_MESSAGE);
                return null;
            }
            transition = new SynchronousTransition(sourceState,
                    destinationState,
                    equivalenceClass);
        }
        return transition;
    }

    /**
     * This method manage the insert of a new Equivalence Class
     */
    public void actionPerformed(ActionEvent e) {
        if (e.getSource().equals(eqClassCombo)) {
            if (eqClassCombo.getSelectedItem().equals("Add new")) {
                boolean exist = false;
                String eqClassName = null;
                Integer durationEqClass = null;
                do {
                    exist = false;
                    eqClassName = JOptionPane.showInputDialog("Equivalence Class name", "Class");
                    for (int i = 0; i < equivalenceClasses.length; i++) {
                        if (equivalenceClasses[i].equals(eqClassName)) {
                            exist = true;
                        }
                    }
                    if (exist) {
                        JOptionPane.showMessageDialog(null, eqClassName + " already exists, choose another name", "ERROR", JOptionPane.ERROR_MESSAGE);
                    } else if (eqClassName != null && ((eqClassName.equals("") || eqClassName.matches(" *")) || eqClassName.equals("Add new"))) {
                        JOptionPane.showMessageDialog(null, "Void String, please re-type Equivalence Class name", "ERROR", JOptionPane.ERROR_MESSAGE);
                        exist = true;
                    }
                } while (exist);
                if(eqClassName != null){
                    Boolean intError = false;
                    do {
                        intError = false;
                        try {
                            durationEqClass = Integer.parseInt(JOptionPane.showInputDialog("Duration", "1"));
                            if(durationEqClass == 0){
                                JOptionPane.showMessageDialog(null, "Invalid duration (0 or null), please re-type duration", "ERROR", JOptionPane.ERROR_MESSAGE);
                                intError = true;
                            }
                        } catch (NumberFormatException exception) {
                            if (durationEqClass != null) {
                                JOptionPane.showMessageDialog(null, "Invalid duration (0 or null), please re-type duration", "ERROR", JOptionPane.ERROR_MESSAGE);
                                intError = true;
                            }
                        }
                    } while (intError);
                    if (durationEqClass != null) {
                        eqClassCombo.removeActionListener(this);
                        eqClassCombo.addItem(eqClassName);
                        EquivalenceClass equivalenceClass = new EquivalenceClass(eqClassName, durationEqClass);
                        equivalenceClassesSet.add(equivalenceClass);
                        eqClassCombo.setSelectedItem(eqClassName);
                        eqClassCombo.validate();
                        eqClassCombo.addActionListener(this);
                        durationField.removeActionListener(this);
                        durationField.setText(Integer.toString(durationEqClass));
                        durationField.validate();
                        durationField.addActionListener(this);
                    }
                }
            }
        }
        mainForm.applyButton.setEnabled(true);
    }

    /**
     * This method delete the itemPanels when the removeButton is clicked
     */
    public void mouseClicked(MouseEvent e) {
        if (mainForm.itemsPanel.getComponentCount() > 2) {
            transition = null;
            mainForm.itemsPanel.remove(this);
            mainForm.itemsPanel.repaint();
            mainForm.itemsPanel.validate();
            mainForm.applyButton.setEnabled(true);
            this.removeMouseListener(this);
        }
        else{
            JOptionPane.showMessageDialog(null, "You can't remove this Transition, there must be one Transition at least", "ERROR", JOptionPane.ERROR_MESSAGE);
        }
    }

    public void mousePressed(MouseEvent e) {
    }

    public void mouseReleased(MouseEvent e) {
    }

    public void mouseEntered(MouseEvent e) {
    }

    public void mouseExited(MouseEvent e) {
    }

    /**
     * This method catch the itemStateChanged event from the synchronous
     * checkbox and enables or disables the equivalence class's combobox and
     * the duration's text field
     */
    public void itemStateChanged(ItemEvent e) {
        if (e.getSource().equals(syncCheck)) {
            if (e.getStateChange() == ItemEvent.DESELECTED) {
                durationField.removeActionListener(this);
                durationField.setEditable(true);
                durationField.addActionListener(this);
                eqClassCombo.removeActionListener(this);
                eqClassCombo.setEnabled(false);
                eqClassCombo.addActionListener(this);
            } else if (e.getStateChange() == ItemEvent.SELECTED) {
                durationField.setEditable(false);
                eqClassCombo.removeActionListener(this);
                eqClassCombo.setEnabled(true);
                eqClassCombo.setModel(new javax.swing.DefaultComboBoxModel(equivalenceClasses));
                eqClassCombo.addItem("Add new");
                eqClassCombo.addActionListener(this);
                durationField.setText(transition.getResidualDuration().toString());
            }
        }
        mainForm.applyButton.setEnabled(true);
    }

    /**
     * This method updates the strings in the comboboxes
     */
    public void setComboBoxes(HashSet<State> _states,
            Set<EquivalenceClass> _equivalenceClass) {
        statesSet = _states;
        states = new String[_states.size()];
        equivalenceClassesSet = _equivalenceClass;
        equivalenceClasses = new String[equivalenceClassesSet.size()];
        int i = 0;
        for (State state : _states) {
            states[i] = state.getName();
            i++;
        }
        i = 0;
        for (EquivalenceClass equivaleClass : _equivalenceClass) {
            equivalenceClasses[i] = equivaleClass.getName();
            i++;
        }
        sourceStateCombo.removeActionListener(this);
        destinationStateCombo.removeActionListener(this);
        durationField.removeActionListener(this);
        sourceStateCombo.setModel(new javax.swing.DefaultComboBoxModel(states));
        sourceStateCombo.setSelectedItem(transition.getSourceState().getName());
        destinationStateCombo.setModel(new javax.swing.DefaultComboBoxModel(states));
        destinationStateCombo.setSelectedItem(transition.getDestinationState().getName());
        if (transition instanceof SynchronousTransition) {
            eqClassCombo.setModel(new javax.swing.DefaultComboBoxModel(equivalenceClasses));
            eqClassCombo.addItem("Add new");
            eqClassCombo.removeActionListener(this);
            eqClassCombo.setSelectedItem(((SynchronousTransition) transition).getEquivalenceClass().getName());
            eqClassCombo.addActionListener(this);
            durationField.setText(((SynchronousTransition) transition).getEquivalenceClass().getDuration().toString());
            durationField.validate();
        }
        sourceStateCombo.addActionListener(this);
        destinationStateCombo.addActionListener(this);
        durationField.addActionListener(this);
    }
}
